/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 */

import { parseJsonFile, Project } from '@ohos/hvigor';

import { TaskNames } from './common/task-names.js';
import { ProjectTaskService } from './service/project-task-service.js';
import { OhosAppTask } from './task/ohos-app-task.js';
import CommonTask = TaskNames.CommonTask;
import fse from 'fs-extra';
import os from 'os';
import path from 'path';

import { OhosPluginId } from '../plugin/common/ohos-plugin-id.js';
import { HarPlugin } from '../plugin/har-plugin.js';
import { HspPlugin } from '../plugin/hsp-plugin.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';

const _log = OhosLogger.getLogger('hvigor-duplicateDependencyCheck');

/**
 * 执行app打包相关任务前，需要针对本地的hsp模块依赖进行校验
 * 如果本地hsp之间存在相同的har依赖，包括本地和远程，则需要进行报错提示
 * 该任务暂时不做增量，因为依赖已经提前收集好了，只需要遍历一次依赖即可
 *
 * @since 2024/05/20
 */
export class DuplicateDependencyCheck extends OhosAppTask {
  private readonly ohPackagePath;
  constructor(project: Project, taskService: ProjectTaskService) {
    super(project, taskService, CommonTask.DUPLICATE_DEPENDENCY_CHECK);
    this.ohPackagePath = taskService.getProjectModel().getOhPackageJson5Path();
  }

  protected doTaskAction(): void {
    const shouldCheck = !!this.service.getBuildOption().strictMode?.duplicateDependencyCheck;
    if (!shouldCheck) {
      return;
    }
    const errors: string[] = [];
    const pkgRoot2PkgName = new Map<string, string>();
    const pkgRoot2ModuleNames = new Map<string, string[]>();
    const localDependencyMap = new Map<string, string>();
    const localHarDependenciesMap = new Map<string, string>();
    this.collectProjectDependencies(localDependencyMap);

    this.project.getSubModules().forEach((value) => {
      const plugin = value.getPluginById(OhosPluginId.OHOS_HAR_PLUGIN);
      if (plugin && plugin instanceof HarPlugin && plugin.getTaskService()?.getModuleModel().isHarModule()) {
        const projectDir = plugin.getModuleModel()?.getProjectDir();
        if (projectDir && localDependencyMap.get(projectDir)) {
          localHarDependenciesMap.set(projectDir, <string>localDependencyMap.get(projectDir));
        }
      }
    });

    this.project.getSubModules().forEach((value) => {
      const plugin = value.getPluginById(OhosPluginId.OHOS_HSP_PLUGIN);
      if (!plugin || !(plugin instanceof HspPlugin)) {
        return;
      }
      const taskService = plugin.getTaskService();
      if (!taskService) {
        return;
      }
      const moduleName = value.getName();

      localHarDependenciesMap.forEach((value, key) => {
        if (!pkgRoot2ModuleNames.has(key)) {
          pkgRoot2ModuleNames.set(key, []);
        }
        pkgRoot2ModuleNames.get(key)!.push(moduleName);
        pkgRoot2PkgName.set(key, value);
      });

      taskService.getHarDependencies().forEach((dependency) => {
        const pkgRoot = dependency.getDependencyRootPath();
        if (!pkgRoot2ModuleNames.has(pkgRoot)) {
          pkgRoot2ModuleNames.set(pkgRoot, []);
        }
        pkgRoot2ModuleNames.get(pkgRoot)!.push(moduleName);
        pkgRoot2PkgName.set(pkgRoot, dependency.getPackageName());
      });
    });
    pkgRoot2ModuleNames.forEach((moduleNames, pkgRoot) => {
      if (moduleNames.length <= 1) {
        return;
      }
      errors.push(`[${moduleNames.join(', ')}] depend on the same HAR [${pkgRoot2PkgName.get(pkgRoot)}].`);
    });
    if (!errors.length) {
      return;
    }
    _log.printErrorExit('FOUND_HSPS_THAT_HAVE_THE_SAME_HAR_DEPENDENCIES', [errors.length > 1 ? 'dependencies' : 'dependency', errors.join(`${os.EOL}`)]);
  }

  private collectProjectDependencies(localDependencyMap: Map<string, string>) {
    if (fse.existsSync(this.ohPackagePath)) {
      const ohPkgObj = parseJsonFile(this.ohPackagePath);
      Object.keys(ohPkgObj.dependencies).forEach((dependenceName) => {
        const dependency = ohPkgObj.dependencies[dependenceName];
        const harPath = /^file:(.*)/i.test(dependency!) ? /^file:(.*)/i.exec(dependency!)![1] : dependency;
        if (!harPath) {
          return;
        }
        const localPath = path.resolve(this.project.getNodeDir(), harPath);
        if (fse.existsSync(localPath)) {
          localDependencyMap.set(localPath, dependenceName);
        }
      });
    }
  }

  initTaskDepends() {
    this.dependsOn(CommonTask.PRE_BUILD_APP.name);
  }
}
